feat(electron): give the bridged coding agent xNet's workspace tools - #638
Merged
Conversation
The desktop app already ran an agent bridge that drove the user's own `claude` CLI as the chat model, but the agent had no way to reach the workspace: the MCP wiring sat behind XNET_BRIDGE_MCP + XNET_BRIDGE_MCP_CLI, and @xnetjs/cli is not an Electron dependency, so that path could never resolve in a packaged app. Chat could talk about your data but not read or write it. Host the MCP server in the main process instead, over the renderer store proxy, and point the agent at it over Streamable HTTP. No CLI to spawn, no local-API hop, no per-session token to plumb across processes. Withhold the tools with XNET_BRIDGE_MCP=0. Also replace the local API's hardcoded four-schema stub with the renderer's real registry, served over the same IPC channel that already carries store operations. Priming retries in the background because both subscribers start before createWindow(); until it succeeds getAllIRIs() throws rather than returning [], so "not ready yet" cannot read as "this workspace has no schemas". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Contributor
🖼️ UI changes in this PRNo visual differences detected in the changed UI. |
Contributor
|
Preview removed for PR #638. |
crs48
added a commit
that referenced
this pull request
Jul 28, 2026
… a face (0406) (#648) Continues exploration **0406** (13/33 after this) — and closes the loop this whole arc opened: **#638** gave desktop Claude Code the workspace tools; this PR finally gives it a UI. ## What this does Moves the AI chat cluster (`AiChatPanel` + nine `ai-*` modules) into `@xnetjs/workbench` under a new subpath export — `@xnetjs/workbench/ai`, kept off the core barrel so hosts opt into the brain/WebLLM weight — and mounts it in the desktop shell: an `assistant` `ShellState` kind, a system-menu entry, and an **Open Assistant** palette command, following the settings/stories pattern exactly. The move was cheap because the cluster was already app-clean (every dependency an `@xnetjs` package) and the panel was written host-aware from the start: it probes `window.xnetAgentBridge` and auto-pairs over IPC. It had simply never been mounted on the one surface that has the bridge. ## Verified in the running desktop app - **Zero-setup pairing**: bridge tier auto-selected, "Running claude · v3.0.0", pairing code auto-filled over IPC. - **The tools arrive**: asked to create a page, the agent declined, *naming `xnet_create_page` as callable* but honoring the read-only tier — 0391's consent posture holding exactly as designed. - **Real reads**: "how many pages titled 'Untitled Page'" → a count plus a genuine node id, verified against the store (`exampleIdReal: true`; the count was one off exact-title equality, consistent with fuzzy search semantics). Approval-gated writes from the panel remain the explicit next step (0394 phase 2) — the panel's own header comment says so. ## Checks 992 web + 89 workbench + 149 electron tests green; repo-wide typecheck clean; changelog fragment included (user-visible feature). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was broken
The desktop app already ran an agent bridge that drives your own
claudeCLI as the chat model. But the agent had no path to your data.The MCP wiring sat behind
XNET_BRIDGE_MCP+XNET_BRIDGE_MCP_CLI, and the CLI entry pointed atxnet mcp serve— except@xnetjs/cliis not an Electron dependency, so that path could never resolve in a packaged app. Even had it resolved, it passed no token, so the local API would have returned 401. Chat could talk about your workspace but not read or write it.What this does
Hosts the MCP server in the Electron main process, over the renderer store proxy, and points the agent at it over Streamable HTTP on an ephemeral loopback port. No CLI to spawn, no local-API hop, no per-session token to plumb across processes. Withhold the tools with
XNET_BRIDGE_MCP=0.Also replaces the local API's hardcoded four-schema stub (
Schema/Task/Project/Note) with the renderer's realschemaRegistry, served over the same IPC channel that already carries store operations. Anything reading that stub was getting a fiction.A subtlety worth reviewing
getAllIRIs()is synchronous inSchemaRegistryAPI, but the registry lives an IPC hop away in the renderer — so the proxy caches it. Both subscribers start beforecreateWindow(), so priming must be background-with-retry and must never be awaited at boot; awaiting it deadlocks startup (hit and backed out during development). While unprimed it throws rather than returning[], so "renderer not ready" cannot read as "this workspace has no schemas".Verification
Verified in the live desktop app over CDP, not just unit-tested.
Bridge status from inside the renderer:
The spawned process, wired as designed:
A real chat turn through the bridge returned "Schema count: 194 · New page ID:
M6_jJ2XVxc5aKIx45SX3V", and the page was really in the store:194 schemas is the proof the registry fix works — the old stub would have reported 4. The test page was deleted afterwards.
Also confirmed empirically:
createMcpHttpServeranswers POST with plainapplication/json(no SSE, nomcp-session-id), and Claude Code 2.1.220 accepts it as a{"type":"http"}MCP server. That was the load-bearing unknown in this design, so it was probed against the real CLI before building on it.Checks:
turbo typecheckclean, 146 electron + 166 devkit tests green (6 new covering the priming logic), lint 0 errors.Notes
xnet_queryschemaId bug found while verifying this landed separately in fix(plugins): honour schemaId in xnet_query and xnet_create #637.@xnetjs/devkitminor — newmcpHttpConfigForexport) plus a changelog fragment.🤖 Generated with Claude Code